﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TasksModel
{
    /// <summary>
    /// Helper class for Time Zone displaying.
    /// </summary>
    public class DateTimeTz
    {
        private DateTime _originalDateTime;

        /// <summary>
        /// In UTC.
        /// </summary>
        public DateTime OriginalDateTime
        {
            [System.Diagnostics.DebuggerStepThrough]
            get { return _originalDateTime; }
        }

        private TimeSpan _utcOffset;

        /// <summary>
        /// Shift from UTC.
        /// </summary>
        public TimeSpan UtcOffset
        {
            [System.Diagnostics.DebuggerStepThrough]
            get { return _utcOffset; }
        }

        /// <summary>
        /// The OriginalDateTime as in this time zone.
        /// </summary>
        public DateTime LocalDateTime
        {
            [System.Diagnostics.DebuggerStepThrough]
            get { return _originalDateTime.Add(_utcOffset); }
        }

        public DateTimeTz(DateTime utc, TimeZoneInfo tz)
        {
            if (utc.Kind == DateTimeKind.Local)
                _originalDateTime = utc.ToUniversalTime();
            else
                _originalDateTime = new DateTime(utc.Ticks, DateTimeKind.Utc);
            // Gets the time difference between the current time zone's standard time and Coordinated Universal Time (UTC)
            _utcOffset = tz.BaseUtcOffset;
            if (tz.IsDaylightSavingTime(_originalDateTime))
            {
                System.TimeZoneInfo.AdjustmentRule rule = GetApplicableRule(utc, tz);
                if (rule != null)
                    _utcOffset += rule.DaylightDelta;
            }
        }

        public DateTimeTz(DateTime utc, string tzId)
            : this(utc, TimeZoneInfo.FindSystemTimeZoneById(tzId))
        {
        }

        public static DateTime Convert(DateTime utc, string tzId)
        {
            return new DateTimeTz(utc, tzId).LocalDateTime;
        }

        public static DateTime ToLocal(DateTime utc)
        {
            return TimeZoneInfo.ConvertTimeFromUtc(utc, TimeZoneInfo.Local);
        }

        public static DateTime ToLocal(DateTime utc, TimeZoneInfo dest)
        {
            return TimeZoneInfo.ConvertTimeFromUtc(utc, dest);
        }

        public static DateTime ToLocal(DateTime utc, string tzIdDest)
        {
            return TimeZoneInfo.ConvertTimeFromUtc(utc, TimeZoneInfo.FindSystemTimeZoneById(tzIdDest));
        }

        public static DateTime ToUTC(DateTime local, TimeZoneInfo source)
        {
            return TimeZoneInfo.ConvertTimeToUtc(local, source);
        }

        public static DateTime ToUTC(DateTime local)
        {
            return TimeZoneInfo.ConvertTimeToUtc(local, TimeZoneInfo.Local);
        }

        public static DateTime ToUTC(DateTime local, string tzIdSrc)
        {
            return TimeZoneInfo.ConvertTimeToUtc(local, TimeZoneInfo.FindSystemTimeZoneById(tzIdSrc));
        }

        private static System.TimeZoneInfo.AdjustmentRule GetApplicableRule(DateTime dateTime, TimeZoneInfo tz)
        {
            DateTime date = dateTime;
            if (dateTime.Kind == DateTimeKind.Utc && tz != TimeZoneInfo.Utc)
                date = date + tz.BaseUtcOffset;
            foreach (System.TimeZoneInfo.AdjustmentRule rule in tz.GetAdjustmentRules())
            {
                if (rule.DateStart > date.Date)
                    return null;
                if (rule.DateEnd < date.Date)
                    continue;
                return rule;
            }
            return null;
        }
    }
}